<template>
  <div class="container">
    <div id="map-container"></div>
    <div class="custom-control">
      <div class="pading-l-15">热点半径：</div>
      <el-slider class="w-180" @input="setRadius" v-model="radius" :min="1" :max="50" />
      <div class="pading-l-15">模糊尺寸：</div>
      <el-slider class="w-180" @input="setBlur" v-model="blur" :min="1" :max="50" />
      <div class="pading-l-15">导出类型：</div>
      <el-select class="margin-lt-15" v-model="exportType" placeholder="请选择导出类型" style="width: 240px">
        <el-option label="png" value="png" />
        <el-option label="pdf" value="pdf" />
      </el-select>
      <div class="pading-l-15" v-show="exportType == 'pdf'">尺寸：</div>
      <el-select class="margin-lt-15" v-show="exportType == 'pdf'" v-model="pdfSize" placeholder="请选择尺寸"
        style="width: 240px">
        <el-option label="A0 (slow)" value="a0" />
        <el-option label="A1" value="a1" />
        <el-option label="A2" value="a2" />
        <el-option label="A3" value="a3" />
        <el-option label="A4" value="a4" />
        <el-option label="A5" value="a5" />
      </el-select>
      <div class="pading-l-15" v-show="exportType == 'pdf'">分辨率：</div>
      <el-select class="margin-lt-15" v-show="exportType == 'pdf'" v-model="pdfDpi" placeholder="请选择分辨率"
        style="width: 240px">
        <el-option label="72 dpi (fast)" value="72" />
        <el-option label="150 dpi" value="150" />
        <el-option label="300 dpi (slow)" value="300" />
      </el-select>
      <el-button @click="exportMap" class="margin-lt-15" size="small" type="primary">{{ `导出${exportType}` }}</el-button>
    </div>
  </div>
</template>
<script setup lang='ts'>
import { onMounted, ref } from 'vue';
import 'ol/ol.css'
import { Map, View, Feature } from 'ol'
import VectorSource from 'ol/source/Vector'
import { Heatmap as HeatmapLayer } from 'ol/layer'
import { Point as GeomPoint } from 'ol/geom'

import { useTianTileLayer, projection } from '@/hooks'
import { TiandiTileLayerConfigEnum } from '@/enum'

import JsPDF from 'jspdf'

import HeatData from './data/coordinates1'

let map: Map,
  heatSource: VectorSource<Feature<GeomPoint>>,
  heatmapLayer: HeatmapLayer

const radius = ref(3)
const blur = ref(4)

// 导出相关
const exportType = ref('png')
const pdfSize = ref('a0')
const pdfDpi = ref('72')

onMounted(() => {
  initMap()
})

// 初始化地图
function initMap() {
  map = new Map({
    target: 'map-container',
    layers: [...useTianTileLayer(TiandiTileLayerConfigEnum.SL)],
    view: new View({
      center: [120.13066322374, 30.240018034923],
      zoom: 13,
      maxZoom: 17,
      minZoom: 1,
      projection: projection,
    })
  })
  getCoordinate()
}

// 获取坐标
function getCoordinate() {
  const coordinates = HeatData
  addHeatmap(coordinates)
}

// 添加热力图
function addHeatmap(coordinates: Array<number[]>) {
  const features: Array<Feature<GeomPoint>> = []
  coordinates.forEach(coordinate => {
    const Heatmap = new Feature({
      type: 'heatmap',
      geometry: new GeomPoint(coordinate)
    })
    features.push(Heatmap)
  })
  heatSource = new VectorSource();
  heatSource.addFeatures(features)
  heatmapLayer = new HeatmapLayer({
    source: heatSource,
    radius: radius.value,
    blur: blur.value,
    gradient: ['#00f', '#0ff', '#0f0', '#ff0', '#f00'],
    zIndex: 1,
    opacity: 1
  })
  map.addLayer(heatmapLayer)
}

// 设置热点半径
function setRadius() {
  if (!heatmapLayer) return
  heatmapLayer.setRadius(radius.value)
}

// 设置模糊尺寸
function setBlur() {
  if (!heatmapLayer) return
  heatmapLayer.setBlur(blur.value)
}

function exportMap() {
  if (exportType.value == 'png') {
    exportPng()
  } else if (exportType.value == 'pdf') {
    exportPdf()
  }
}

// 导出地图PNG
function exportPng() {
  map.once('rendercomplete', function () {
    const mapCanvas = document.createElement('canvas') as HTMLCanvasElement;
    const size = map.getSize()!;
    mapCanvas.width = size[0];
    mapCanvas.height = size[1];
    const mapContext = mapCanvas.getContext('2d')!;
    Array.prototype.forEach.call(
      map.getViewport().querySelectorAll('.ol-layer canvas, canvas.ol-layer'),
      function (canvas) {
        if (canvas.width > 0) {
          const opacity =
            canvas.parentNode.style.opacity || canvas.style.opacity;
          mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
          let matrix;
          const transform = canvas.style.transform;
          if (transform) {
            // Get the transform parameters from the style's transform matrix
            matrix = transform
              .match(/^matrix\(([^\(]*)\)$/)[1]
              .split(',')
              .map(Number);
          } else {
            matrix = [
              parseFloat(canvas.style.width) / canvas.width,
              0,
              0,
              parseFloat(canvas.style.height) / canvas.height,
              0,
              0,
            ];
          }
          // Apply the transform to the export map context
          CanvasRenderingContext2D.prototype.setTransform.apply(
            mapContext,
            matrix
          );
          const backgroundColor = canvas.parentNode.style.backgroundColor;
          if (backgroundColor) {
            mapContext.fillStyle = backgroundColor;
            mapContext.fillRect(0, 0, canvas.width, canvas.height);
          }
          mapContext.drawImage(canvas, 0, 0);
        }
      }
    );
    mapContext.globalAlpha = 1;
    mapContext.setTransform(1, 0, 0, 1, 0, 0);
    const link = document.createElement('a')
    link.href = mapCanvas.toDataURL();
    link.download = 'map.png'
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
  })
  map.renderSync()
}

// 导出地图PDF
function exportPdf() {
  const dims: { [k: string]: any } = {
    a0: [1189, 841],
    a1: [841, 594],
    a2: [594, 420],
    a3: [420, 297],
    a4: [297, 210],
    a5: [210, 148],
  };
  const dim = dims[pdfSize.value];
  const width = Math.round((dim[0] * Number(pdfDpi.value)) / 25.4);
  const height = Math.round((dim[1] * Number(pdfDpi.value)) / 25.4);
  const size = map.getSize()!;
  const viewResolution = map.getView().getResolution()!;

  map.once('rendercomplete', function () {
    const mapCanvas = document.createElement('canvas');
    mapCanvas.width = width;
    mapCanvas.height = height;
    const mapContext = mapCanvas.getContext('2d')!;
    Array.prototype.forEach.call(
      document.querySelectorAll('.ol-layer canvas'),
      function (canvas) {
        if (canvas.width > 0) {
          const opacity = canvas.parentNode.style.opacity;
          mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
          const transform = canvas.style.transform;
          // Get the transform parameters from the style's transform matrix
          const matrix = transform
            .match(/^matrix\(([^\(]*)\)$/)[1]
            .split(',')
            .map(Number);
          // Apply the transform to the export map context
          CanvasRenderingContext2D.prototype.setTransform.apply(
            mapContext,
            matrix
          );
          mapContext.drawImage(canvas, 0, 0);
        }
      }
    );
    mapContext.globalAlpha = 1;
    mapContext.setTransform(1, 0, 0, 1, 0, 0);
    const pdf = new JsPDF('landscape', undefined, pdfSize.value);
    pdf.addImage(
      mapCanvas.toDataURL('image/jpeg'),
      'JPEG',
      0,
      0,
      dim[0],
      dim[1]
    );
    pdf.save('map.pdf');
    // Reset original map size
    map.setSize(size);
    map.getView().setResolution(viewResolution);
    document.body.style.cursor = 'auto';
  });

  // Set print size
  const printSize = [width, height];
  map.setSize(printSize);
  const scaling = Math.min(width / size[0], height / size[1]);
  map.getView().setResolution(viewResolution / scaling);
}
</script>
<style scoped>
.container {
  width: 100%;
  height: 100%;
}

#map-container {
  width: 100%;
  height: calc(100% - 45px);
}

.custom-control {
  width: 100%;
  height: 45px;
  display: flex;
  align-items: center;
}

.pading-l-15 {
  padding-left: 15px;
}

.w-180 {
  width: 180px;
}

.margin-lt-15 {
  margin-left: 15px;
}
</style>